package ro.prjmanager.service.resolvers;

import ro.prjmanager.dao.ColumnsDao;
import ro.prjmanager.dao.ForeignModelDao;
import ro.prjmanager.dao.ModelsDao;
import ro.prjmanager.dao.TableSetDao;
import ro.prjmanager.model.Columns;
import ro.prjmanager.model.ForeignModel;
import ro.prjmanager.model.Models;
import ro.prjmanager.model.TableSet;

import java.util.*;

@SuppressWarnings({"ALL"})
public class SynchResolver {
    public int level = 0;
    public static final String C_AFFECTED = "AFFECTED_C";
    public static final String FM_AFFECTED = "AFFECTED_FM";
    public static final String M_AFFECTED = "AFFECTED_M";

    private HashMap<String, ArrayList> affected;

    public SynchResolver(TableSet entity, boolean deleteAlsoTS) {
        entities = new ArrayList<String>();
        level++;
        affected = new HashMap<String, ArrayList>();
        affected.put(C_AFFECTED, new ArrayList<Columns>());
        affected.put(FM_AFFECTED, new ArrayList<ForeignModel>());
        affected.put(M_AFFECTED, new ArrayList<Models>());
        this.currentEntity = entity;
        this.deleteAlsoTS = deleteAlsoTS;
    }
    public SynchResolver(List<TableSet> entityList, boolean deleteAlsoTS) {
        entities = new ArrayList<String>();
        level++;
        affected = new HashMap<String, ArrayList>();
        affected.put(C_AFFECTED, new ArrayList<Columns>());
        affected.put(FM_AFFECTED, new ArrayList<ForeignModel>());
        affected.put(M_AFFECTED, new ArrayList<Models>());
        this.currentEntityList = entityList;
        this.deleteAlsoTS = deleteAlsoTS;
    }


    public SynchResolver(TableSet entity, HashMap<String, ArrayList> affectedParent) {

        affected = new HashMap<String, ArrayList>();
        affected.put(C_AFFECTED, new ArrayList<Columns>());
        affected.put(FM_AFFECTED, new ArrayList<ForeignModel>());
        affected.put(M_AFFECTED, new ArrayList<Models>());
        this.currentEntity = entity;

    }

    private ForeignModelDao foreignModelDao;
    private ColumnsDao columnsDao;
    private ModelsDao modelsDao;
    private TableSetDao tableSetDao;


    public void setForeignModelDao(ForeignModelDao foreignModelDao) {
        this.foreignModelDao = foreignModelDao;
    }

    public void setColumnsDao(ColumnsDao columnsDao) {
        this.columnsDao = columnsDao;
    }

    public void setModelsDao(ModelsDao modelsDao) {
        this.modelsDao = modelsDao;
    }

    public TableSetDao getTableSetDao() {
        return tableSetDao;
    }

    public void setTableSetDao(TableSetDao tableSetDao) {
        this.tableSetDao = tableSetDao;
    }

    private TableSet currentEntity;
    private List<TableSet> currentEntityList;
    private boolean deleteAlsoTS;

    private static final int REMOVE = 1;
    private static final int SYNCH = 2;
    private static final int ADD = 3;
    private static final int FIRST = 0;

    public TableSet getCurrentEntity() {
        return currentEntity;
    }

    public void setCurrentEntity(TableSet currentEntity) {
        this.currentEntity = currentEntity;
    }

    public List<TableSet> getCurrentEntityList() {
        return currentEntityList;
    }

    public void setCurrentEntityList(List<TableSet> currentEntityList) {
        this.currentEntityList = currentEntityList;
    }

    private boolean hasFounded(Columns col) {
        for (Object colO : affected.get(C_AFFECTED)) {
            if (((Columns) colO).getId().equals(col.getId()))
                return true;
        }
        return false;
    }

    private boolean hasFounded(Models model) {
        for (Object modelO : affected.get(M_AFFECTED)) {
            if (((Models) modelO).getId().equals(model.getId()))
                return true;
        }
        return false;
    }

    private boolean hasFounded(ForeignModel fModel) {
        for (Object fModelO : affected.get(FM_AFFECTED)) {
            if (((ForeignModel) fModelO).getId().equals(fModel.getId()))
                return true;
        }
        return false;
    }

    public List<Models> getAffectedModels() {
        return affected.get(M_AFFECTED);
    }

    public HashMap<String, ArrayList> getAffected() {
        return affected;
    }

    public void setAffected(HashMap<String, ArrayList> affected) {
        this.affected = affected;
    }

    private static List<String> entities = new ArrayList<String>();

    private void recurrentSearch(Columns col) {
        if (col == null || hasFounded(col))
            return;
        if (hasFounded(col))
            return;
        boolean first = col.getRelationColumns() != null && !col.getRelationColumns().isEmpty();
        if (first)
            affected.get(C_AFFECTED).add(col);
        if (col.getOwnerTable() != null && !hasFounded(col.getOwnerTable())) {
            affected.get(M_AFFECTED).add(col.getOwnerTable());
        }
        if (col.getFkForeignModel() != null && !hasFounded(col.getFkForeignModel())) {
            affected.get(FM_AFFECTED).add(col.getFkForeignModel());

            if (!entities.contains(col.getFkForeignModel().getDatabaseName())) {
                TableSet tableSet = modelsDao.findByDataBaseName(col.getFkForeignModel().getDatabaseName());
                if(tableSet!=null){
                    entities.add(col.getFkForeignModel().getDatabaseName());
                SynchResolver synchResolver = new SynchResolver(
                        tableSet, affected
                );
                /* if(synchResolver.level>2)
                return;*/

                synchResolver.setColumnsDao(columnsDao);
                synchResolver.setForeignModelDao(foreignModelDao);
                synchResolver.setModelsDao(modelsDao);
                if(((Models)tableSet).getColumns()!=null&&((Models)tableSet).getColumns().size()>0)
                    synchResolver.recurrentSearch(((Models) tableSet).getColumns().get(0));
                this.affected.get(M_AFFECTED).addAll(
                        synchResolver.getAffected().get(M_AFFECTED)
                );
                this.affected.get(FM_AFFECTED).addAll(
                        synchResolver.getAffected().get(FM_AFFECTED)
                );
                this.affected.get(C_AFFECTED).addAll(
                        synchResolver.getAffected().get(C_AFFECTED)
                );

            }
            }

        }
        recurrentSearch(col.getForeignColumn());
        List<Columns> relations = col.getRelationColumns();
        if (relations != null && !relations.isEmpty())
            for (Columns colR : relations) {
                recurrentSearch(colR);
            }
        if (!first)
            affected.get(C_AFFECTED).add(col);
        if (col.getOwnerTable() == null)
            return;
        List<Columns> cols = col.getOwnerTable().getColumns();
        if (cols == null || cols.isEmpty())
            return;

        for (Columns cc : cols) {
            recurrentSearch(cc);
        }
    }

    private Deque<Columns> columnses = new ArrayDeque<Columns>();

    private void deleteColumns() {
        /* int p=0;
        int q=0;
        System.out.println(p/q);*/
        if (columnses.isEmpty())
            return;

        Columns col = columnses.pollLast();
        try {
            columnsDao.executeDML("delete from columns where id=" + col.getId());
        } catch (Exception e) {
            columnses.push(col);
            System.out.println("Could not delete column " + col.getDatabaseColumnName() + "(" + col.getId() + ") reentering it in stack");
        } finally {
            deleteColumns();
        }
    }

    public void cleanAffectedEntitiesList(){
           for(TableSet tableSet_:currentEntityList){
               TableSet tableSet=tableSetDao.find(tableSet_.getId(), false);    
               if(tableSet instanceof Models){
                    Models mod=modelsDao.find(tableSet.getId(),false);
                    List<Columns> columns = mod.getColumns();
                    if (columns != null && !columns.isEmpty()) {
                        recurrentSearch(columns.get(FIRST));
                    }
               }
           }
            //deleting afectted columns
            List<Columns> columnsToBeRemoved = affected.get(C_AFFECTED);
            if (columnsToBeRemoved != null && !columnsToBeRemoved.isEmpty()) {
                columnses.addAll(columnsToBeRemoved);
                deleteColumns();
            }
            //deleting foreign model
            List<ForeignModel> foreignModelsToBeRe = affected.get(FM_AFFECTED);
            if (foreignModelsToBeRe != null && !foreignModelsToBeRe.isEmpty()){
                 Set<Integer> ids=new HashSet<Integer>();
                for(ForeignModel fm:foreignModelsToBeRe){
                    ids.add(fm.getId());
                }
                for(Integer id:ids){
                    foreignModelDao.executeDML("delete from foreign_model where id="+id);
                }
            }
            //deleting models
            List<Models> modelsList = affected.get(M_AFFECTED);
            if (modelsList != null && !modelsList.isEmpty()){
                Set<Integer> ids=new HashSet<Integer>();
            if(deleteAlsoTS)
                modelsDao.executeDML("delete from models where id="+currentEntity.getId());
            }



        if (deleteAlsoTS)
            tableSetDao.executeDML("delete from table_set where id="+currentEntity.getId());
    }

    public void cleanAffectedEntities() {
        if (currentEntity instanceof Models) {
            List<Columns> columns = ((Models) currentEntity).getColumns();
            if (columns != null && !columns.isEmpty()) {
                recurrentSearch(columns.get(FIRST));
            }
            //deleting afectted columns
            List<Columns> columnsToBeRemoved = affected.get(C_AFFECTED);
            if (columnsToBeRemoved != null && !columnsToBeRemoved.isEmpty()) {
                columnses.addAll(columnsToBeRemoved);
                deleteColumns();
            }
            //deleting foreign model
            List<ForeignModel> foreignModelsToBeRe = affected.get(FM_AFFECTED);
            if (foreignModelsToBeRe != null && !foreignModelsToBeRe.isEmpty()){
                 Set<Integer> ids=new HashSet<Integer>();
                for(ForeignModel fm:foreignModelsToBeRe){
                    ids.add(fm.getId());
                }
                for(Integer id:ids){
                    foreignModelDao.executeDML("delete from foreign_model where id="+id);;
                }
            }
            //deleting models
            List<Models> modelsList = affected.get(M_AFFECTED);
            if (modelsList != null && !modelsList.isEmpty()){
                Set<Integer> ids=new HashSet<Integer>();
            if(deleteAlsoTS)    
                modelsDao.executeDML("delete from models where id="+currentEntity.getId());
            }


        }
        if (deleteAlsoTS)
            tableSetDao.executeDML("delete from table_set where id="+currentEntity.getId());

    }
}
